home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Scheduling / Cassandra / Source / Clock.m < prev    next >
Encoding:
Text File  |  1990-12-02  |  10.8 KB  |  454 lines

  1. // Clock.m, simple clock view 
  2. // Author: Ali T. Ozer, NeXT Developer Support Group
  3. // Created: May 26, 1989 (for version 0.9)
  4. // Modified: June 14 and Aug 14, 1989 (for version 1.0)
  5. // 
  6. // Modifier: Jiro Nakamura, Independent NeXT Developer
  7. // Modified: May 11, 1990 (for Cassandra)
  8. //
  9. // Subclass of view to implement a simple analog or digital clock. This view is
  10. // pretty generic and can probably be added to any program. Different clock
  11. // faces can be set through the setBackgroundGray: and setClockGray: methods;
  12. // you can also specify TIFF files as background images for the clock through
  13. // the setClockFaceToBitmapNamed: method. Finally you have the option of
  14. // turning the seconds hand on or off. 
  15. static char authorid[] = "$Author: jiro $";
  16. static char rcsid[] = "$Id: Clock.m,v 1.3 90/12/03 01:55:16 jiro Exp Locker: jiro $";
  17.  
  18. #import "Clock.h"
  19. #import "ClockPS.h"    // PSwrap routines
  20.  
  21. #import <dpsclient/wraps.h>    
  22. #import <appkit/nextstd.h>
  23. #import <appkit/Application.h>
  24. #import <appkit/Button.h>
  25. #import <appkit/Window.h>
  26. #import <objc/typedstream.h>
  27. #import <string.h>
  28. #import <sys/time.h>        
  29. #import "calendar.h"
  30.  
  31. #define MAX_CVIEW_WIDTH    14
  32.  
  33. @implementation Clock
  34. // ShowTime() is the timed entry function called by the
  35. // timed entry mechanism. It first writes the time out on the face of the 
  36. // clock, and then reinstalls the timed entry if the clock is not showing the
  37. // seconds. If the seconds are being shown, no need to reinstall the timed
  38. // entry; a second skipped here and then won't matter. If minutes are being
  39. // shown, we want to make sure that the minute jumps at the next top of the
  40. // minute, regardless of how long it took to service the timed entry.
  41.  
  42. void ShowTime (teNum, now, clock) 
  43. DPSTimedEntry teNum;
  44. double now;
  45. id clock;
  46. {
  47.     [clock display];        
  48.     if ([clock showSeconds] == NO) [[clock stopTimedEntry] startTimedEntry:NO];
  49. }
  50.  
  51. // newFrame creates the view and initializes the various paramaters. The
  52. // constants below determine the lengths of the clock hands.
  53.  
  54. #define HOURRATIO    0.2    /* Hour hand length compared face size */
  55. #define MINUTERATIO    0.35    /* Minute & seconds hands */
  56.  
  57. + newFrame:(const NXRect *)frm
  58. {
  59.     self = [super newFrame:frm]; 
  60.  
  61.     // Set the default state (no special face, no seconds, grays).
  62.     clockFace = nil;
  63.     showSeconds = NO;
  64.     showDate = NO;
  65.     backgroundGray = NX_LTGRAY;
  66.     clockGray = NX_BLACK;
  67.     [self computeNewSizes];
  68.             
  69.     // Start the time entry. YES indicates that this is the first time.
  70.     [self startTimedEntry:YES];
  71.     
  72.     return self;
  73. }
  74.  
  75. // Good idea to get rid of the timed entry while freeing...
  76.  
  77. - free
  78. {
  79.     [self stopTimedEntry];
  80.     return [super free];
  81. }
  82.  
  83. // startTimedEntry will install the timed entry. If fireASAP is YES, the
  84. // timed entry is set to fire off as soon as possible (this would be the case
  85. // at the start of the program, for instance). If fireASAP is NO, then the
  86. // timed entry is set to fire off in one second (if seconds are being shown)
  87. // or at the top of the next minute (in anytime between 0 and 60 seconds).
  88.  
  89. - startTimedEntry:(BOOL)fireASAP
  90. {
  91.     double fireIn;
  92.  
  93.     if (fireASAP) fireIn = 0.0;          // Fire as soon as possible!
  94.     else if (showSeconds) fireIn = 1.0;      // Fire in a second (good enough)
  95.     else {
  96.         struct timeval currentTime;
  97.         gettimeofday (¤tTime, NULL);
  98.     fireIn = 60.0 - (currentTime.tv_sec % 60);  // Top of the minute
  99.     }
  100.     
  101.     clockTE = DPSAddTimedEntry(fireIn, &ShowTime, self, NX_MODALRESPTHRESHOLD);
  102.  
  103.     return self;
  104. }
  105.  
  106. // stopTimedEntry stops the timed entry. Don't call this method unless
  107. // the timed entry is installed an running.
  108.  
  109. - stopTimedEntry
  110. {
  111.     DPSRemoveTimedEntry (clockTE);
  112.     return self;
  113. }
  114.  
  115. // drawSelf:: displays the face of the clock, showing the correct time.
  116. // The seconds hand will be shown only if desired.
  117.  
  118. - drawSelf:(NXRect *)rects :(int)rectCount
  119. {    
  120.     int min, hour, sec;
  121.     struct tm *localTime;
  122.     struct timeval currentTime;
  123.     static char buf[10];
  124.     extern const char *shortWeekDays[7];
  125.     extern const char *shortMonths[12];
  126.     
  127.     // Additions by Jiro for target/action response
  128.     //Send the <action> to the <target>.
  129.     // Mod Jan 6, 1990
  130.        [target    perform:action  with:self];
  131.     
  132.     gettimeofday (¤tTime, NULL);
  133.     localTime = localtime (&(currentTime.tv_sec));
  134.     min  = localTime->tm_min;
  135.     hour = localTime->tm_hour; 
  136.     sec  = localTime->tm_sec;
  137.  
  138.     // Erase the background and composite the clock face 
  139.     // in if there is one.
  140.     // Note that we erase even though we might have a bitmap; 
  141.     // this way if the
  142.     // bitmap is smaller than the view it'll still look OK. In a more
  143.     // efficient frame of mind, one could try to avoid unnecessary fills.
  144.  
  145.     if (backgroundGray >= 0.0 && backgroundGray <= 1.0) {
  146.         PSsetgray (backgroundGray);
  147.         NXRectFill (&bounds);
  148.         }
  149.         
  150.     // If we have a clockface, let's display it
  151.     if(clockFace != nil) 
  152.         {
  153.         #ifdef DEBUG
  154.             fprintf(stderr,"%s: Compositing clock face.\n",
  155.                  PROGNAME);
  156.         #endif
  157.         [clockFace composite:NX_COPY toPoint:&compositeTo];
  158.         }
  159.  
  160.     switch( clockType)
  161.         {
  162.         case CLOCK_ANALOG:
  163.             // Display the seconds arm, if desired, and then 
  164.             // the hours and minutes.
  165.             if (showSeconds)
  166.                 drawClockHand (-6.0 * sec, minuteLen, 
  167.                     clockGray, 0.0);
  168.                drawClockHand (- (hour + min / 60.0) * 30.0, hourLen,
  169.                  clockGray, 2.0);
  170.                drawClockHand (- fmod(min, 60.0) * 6.0, minuteLen,
  171.                 clockGray, 2.0);
  172.             if( showDate)
  173.                 {
  174.                   sprintf(buf, "%s %2d",
  175.                     shortMonths[localTime->tm_mon],
  176.                        localTime->tm_mday);
  177.                    drawClockString( 0, 7, 8, buf);
  178.                     drawClockString( 0, -10, 8, (char *) 
  179.                     shortWeekDays[localTime->tm_wday]);
  180.                 }
  181.             break;
  182.         case CLOCK_DIGITAL:
  183.             if( !militaryTime)
  184.                 {            
  185.                 if( hour >= 12 )
  186.                     hour -= 12;
  187.                 if( hour == 0 )
  188.                     hour = 12;            
  189.                 }
  190.                         
  191.             sprintf(buf, "%2d:%02d", hour, min);
  192.             drawClockString( 0, 6, 18, buf);
  193.             sprintf(buf,"%s %s %2d", (char *) 
  194.             shortWeekDays[localTime->tm_wday],
  195.             shortMonths[localTime->tm_mon], localTime->tm_mday);
  196.             drawClockString( 0, -1, 8, buf);
  197.             PSmoveto( -10, -5);
  198.             PSlineto( 10, -5);
  199.             PSstroke();
  200.             drawClockString( 0, -14, 8, eventTime);
  201.             drawClockString( 0, -21, 8, eventMessage);
  202.             
  203.             break;
  204.         default:
  205.             fprintf(stderr,"%s: Unrecognized clock type: %d\n",
  206.                 PROGNAME, clockType);
  207.         }    
  208.     return self;
  209. }
  210.  
  211. // Set the clock face to the specified bitmap. If bitmapName is NULL,
  212. // then we have no clock face. 
  213.  
  214. - setClockFaceToBitmapNamed:(const char *)bitmapName
  215. {
  216.  
  217.     #ifdef DEBUG
  218.         fprintf(stderr, "%s: Setting clockface bitmap to %s\n",
  219.                 PROGNAME, bitmapName);
  220.     #endif
  221.     
  222.     if (bitmapName && (clockFace = [NXImage findImageNamed:bitmapName])) 
  223.         {
  224.         [self computeBitmapLocation];
  225.         }
  226.     else 
  227.            {
  228.         #ifdef DEBUG
  229.             fprintf(stderr,"%s: Couldn't find a icon for %s.\n",
  230.                 PROGNAME, bitmapName);
  231.         #endif
  232.         clockFace = nil;
  233.             }
  234.        
  235.     [self display];
  236.  
  237.     return self;
  238. }
  239.  
  240. // Compute and cache the location where the clock face should be
  241. // composited into. This method should be invoked everytime the clock face 
  242. // changes or the view is resized.
  243. //
  244. // We assume that the view is translated so that the origin is at the center.
  245.  
  246. - computeBitmapLocation
  247. {
  248.     if (clockFace) {
  249.         NXSize bitmapSize;
  250.         [clockFace getSize:&bitmapSize];
  251.         compositeTo.x = - bitmapSize.width / 2.0;
  252.         compositeTo.y = - bitmapSize.height / 2.0;
  253.     }
  254.     return self;
  255. }
  256.  
  257.  
  258. // Figure the lengths of the hands (based on the size of the view) and
  259. // translate the view so that the center is (0,0).
  260.  
  261. - computeNewSizes
  262. {
  263.     // Translate view so that the center is 0,0.
  264.     [self setDrawOrigin:-bounds.size.width/2.0 :-bounds.size.height/2.0];
  265.  
  266.     // Compute the hand lengths and cache bitmap location.
  267.     [self computeBitmapLocation];
  268.     minuteLen = MIN(bounds.size.width, bounds.size.height) * MINUTERATIO;
  269.     hourLen = MIN(bounds.size.width, bounds.size.height) * HOURRATIO;
  270.  
  271.     return self;
  272. }
  273.  
  274. // Overriding sizeTo:: allows us to resize and fix up the clock whenever
  275. // the size is changed.
  276.  
  277. - sizeTo:(NXCoord)w :(NXCoord)h
  278. {
  279.     [super sizeTo:w :h];
  280.     [self computeNewSizes];
  281.     return self;
  282. }
  283.  
  284. // setShowSecondsToBool: sets whether or not the seconds hand is shown.
  285. // The timed entry must be reinstalled whenever this setting is changed
  286. // as time to the next firing changes.
  287.  
  288. - setShowSecondsToBool:(BOOL)seconds
  289.     showSeconds = seconds;
  290.     [[self stopTimedEntry] startTimedEntry:NO];
  291.     [self display];
  292.  
  293.     return self;
  294. }
  295.  
  296. - setShowDateToBool: (BOOL) date
  297. {
  298.     showDate  = date;
  299.     [[self stopTimedEntry] startTimedEntry:NO];
  300.     [self display];
  301.  
  302.     return self;
  303. }
  304.  
  305. - setMilitaryTimeToBool: (BOOL) mt
  306. {
  307.     militaryTime = mt;
  308.     [[self stopTimedEntry] startTimedEntry:NO];
  309.     [self display];
  310.  
  311.     return self;
  312. }
  313.  
  314. - setClockTypeToInt: (int) type
  315. {
  316.     clockType = type;
  317.     [[self stopTimedEntry] startTimedEntry:NO];
  318.     [self display];
  319.  
  320.     return self;
  321. }
  322.  
  323.  
  324. // Methods to set/get background and clock hand colors.
  325.  
  326. - setBackgroundGrayToFloat:(float)gray 
  327. {
  328.     backgroundGray = gray;
  329.     [self display];
  330.  
  331.     return self;
  332. }
  333.  
  334. - setClockGrayToFloat:(float)gray
  335. {
  336.     clockGray = gray;
  337.     [self display];
  338.  
  339.     return self;
  340. }
  341.  
  342. - setMessage: (char *) buf1 : (char *) buf2
  343. {
  344.     strncpy( eventTime, buf1, MAX_CVIEW_WIDTH);
  345.     strncpy( eventMessage, buf2, MAX_CVIEW_WIDTH);
  346.     
  347.     if( clockType == CLOCK_DIGITAL)
  348.         [self display];
  349.     return self;
  350. }
  351.  
  352. // Methods to return the values of the above parameters
  353.  
  354. - (BOOL)showSeconds 
  355.     return showSeconds; 
  356. }
  357.  
  358. - (BOOL)showDate 
  359.     return showDate; 
  360. }
  361.  
  362. - (const char *)clockFaceBitmapName 
  363.     return [clockFace name]; 
  364. }
  365.  
  366. - (float)backgroundGray
  367.     return backgroundGray; 
  368. }
  369.  
  370. - (float)clockGray 
  371.     return clockGray; 
  372. }
  373.  
  374. // Finally, the IB-callable (target/action) versions of the above methods.
  375.  
  376. - setClockFace:sender
  377. {
  378.     return [self setClockFaceToBitmapNamed:[sender stringValue]];
  379. }
  380.  
  381. - setShowSeconds:sender
  382. {
  383.     return [self setShowSecondsToBool:[sender state]];
  384. }
  385.  
  386. - setShowDate:sender
  387. {
  388.     return [self setShowDateToBool:[sender state]];
  389. }
  390.  
  391. - setBackgroundGray:(id)sender
  392. {
  393.     return [self setBackgroundGrayToFloat:[sender floatValue]];
  394. }
  395.     
  396. - setClockGray:(id)sender
  397. {
  398.     return [self setClockGrayToFloat:[sender floatValue]];
  399. }
  400.  
  401. // Additions to allow target/action response
  402. // Mods by Jiro Nakamura, Jan 6, 1990
  403. - setTarget: (id) aTarget
  404. {
  405.     target = aTarget;
  406.     return self;
  407. }
  408.  
  409. - setAction: (SEL) anAction
  410. {
  411.     action = anAction;
  412.     return self;
  413. }
  414.  
  415. // Archiving methods. 
  416.  
  417. - awake
  418. {
  419.     [self computeNewSizes];
  420.     [self startTimedEntry:YES];
  421.     return self;
  422. }
  423.  
  424. - write:(NXTypedStream *)stream
  425. {
  426.     const char *bitmapName = [self clockFaceBitmapName];
  427.  
  428.     [super write:stream];
  429.     NXWriteTypes (stream, "ffi*", 
  430.           &backgroundGray, &clockGray, &showSeconds, &bitmapName);
  431.     return self;
  432. }
  433.  
  434. - read:(NXTypedStream *)stream
  435. {
  436.     char *bitmapName;
  437.  
  438.     [super read:stream];
  439.     NXReadTypes (stream, "ffi*",
  440.           &backgroundGray, &clockGray, &showSeconds, &bitmapName);
  441.     [self setClockFaceToBitmapNamed:bitmapName];
  442.     if (bitmapName) free (bitmapName);
  443.  
  444.     return self;
  445. }
  446.  
  447. @end
  448.